from machine import UART, I2C, Pin, ADC, PWM
from ina219 import INA219, DeviceRangeError
import time
import _thread
import network
import socket
import machine
import math
import asyncio

# import uasyncio
# 获取站点WiFi接口的实例并将其存储在变量上
station = network.WLAN(network.STA_IF)
clientID = "4892bd7fe005ecbbccf35929157ec7e7"  # Client ID ，密钥，巴法云控制台获取
serverIP = "bemfa.com"  # 服务器地址
port = 8344  # 服务器端口
client = None

i2c = I2C(0, scl=Pin(1), sda=Pin(0), freq=400_000)
uart = UART(1, baudrate=57600, tx=10, rx=3)

# 配置定时器
timer1 = machine.Timer(0)
timer2 = machine.Timer(1)

"""分路电阻值，单位为欧姆（强制）"""
SHUNT_OHMS = 0.005
"""以安培为单位的最大预期电流,可选"""
MAX_EXPECTED_AMPS = 1

"""电阻值，通讯协议，电流值"""
ina = INA219(SHUNT_OHMS, i2c, MAX_EXPECTED_AMPS)

"""配置和校准INA219的测量方式OUT输出12位"""
ina.configure(ina.RANGE_32V)

f = u = a = p = c = oa = ob = U_k = I_k = P_k = T_k = MR = ah = wh = Ah_k = Wh_k = tm = out_k = 0
time_k = ""

U_f = I_f = P_f = T_f = Ah_f = Wh_f = out_f = out_c = ca = cb = 0
time_f = ""

ka = kb = kc = 0
error = ""
auto = 0
ktdy = 0
fasong = 0
xunhuan = 0
xunhuancs = 0

# 初始化 ADC
adc = machine.ADC(machine.Pin(4))  # 假设温度传感器连接到引脚 4
adc.atten(ADC.ATTN_11DB)

OUT = Pin(12, Pin.IN)  # 输出 #右边指示灯
OUT_k = out_k = OUT.value()

KF = Pin(13, Pin.IN)  # 工作模式 #左边指示灯
kf = KF.value()
ktdy = kc = kf

chongdian = Pin(6, Pin.OUT)  # 发送串口数据使能引脚
chongdian.value(0)

tx = Pin(7, Pin.OUT)  # 发送串口数据使能引脚
tx.value(0)

FAN = PWM(Pin(8,Pin.OUT))    # 风扇控制
FAN.freq(10000)
FAN.duty(0)


def Resetcharts():
    with open("ktdy.txt", "w") as f:
        f.write("")
    with open("fdy.txt", "w") as f:
        f.write("")
    with open("ktdy_data.txt", "w") as f:
        f.write(str(ah) + "\n")
        f.write(str(wh) + "\n")
        f.write(str(tm) + "\n")
    with open("ktdy_auto.txt", "w") as f:
        f.write(str(auto) + "\n")
        f.write(str(ktdy) + "\n")
        f.write(str(xunhuancs) + "\n")


def Readcharts():
    global auto, ktdy, ah, wh, tm, xunhuancs
    with open("ktdy_data.txt", "r") as f:
        lines = f.readlines()
        ah = float(lines[0].strip())
        wh = float(lines[1].strip())
        tm = int(lines[2].strip())
        print("安时:", ah)
        print("瓦时:", wh)
        print("时间:", tm)

    with open("ktdy_auto.txt", "r") as f:
        lines = f.readlines()
        auto = int(lines[0].strip())
        ktdy = int(lines[1].strip())
        xunhuancs = int(lines[2].strip())
        print("auto:", auto)
        print("ktdy:", ktdy)
        print("xunhuancs:", xunhuancs)

    with open('boot_count.txt', 'w') as f:
        f.write(str(0))


def ktdy_auto():
    with open("ktdy_auto.txt", "w") as f:
        f.write(str(auto) + "\n")
        f.write(str(ktdy) + "\n")
        f.write(str(xunhuancs) + "\n")


def hex4_to_hex2(hex_str):
    a = []
    if len(hex_str) != 4:
        raise ValueError("输入必须是 4 位十六进制数")
    high_nibble = int(hex_str[:2], 16)  # 取高 4 位并转换为十进制
    low_nibble = int(hex_str[2:], 16)  # 取低 4 位并转换为十进制
    a.append(low_nibble)
    a.append(high_nibble)
    return a


def hex6_to_hex2(hex_str):
    a = []
    if len(hex_str) != 6:
        raise ValueError("输入必须是 6 位十六进制数")
    high_nibble = int(hex_str[:2], 16)  # 取高 4 位并转换为十进制
    nibble = int(hex_str[2:4], 16)  # 取低 4 位并转换为十进制
    low_nibble = int(hex_str[4:6], 16)  # 取低 4 位并转换为十进制
    a.append(low_nibble)
    a.append(nibble)
    a.append(high_nibble)
    return a


def convert_range(value, source_min, source_max, target_min, target_max):
    source_range = source_max - source_min
    target_range = target_max - target_min
    value = min(max(value, source_min), source_max)
    value_scaled = (value - source_min) / source_range
    return target_min + (value_scaled * target_range)


def connect_wlan(ssid, password):
    station = network.WLAN(network.STA_IF)
    ap_if = network.WLAN(network.AP_IF)
    station.active(True)
    ap_if.active(False)
    try:
        if not station.isconnected():
            print("Connecting to WLAN ({})...".format(ssid))
            station.active(True)
            station.connect(ssid, password)
    except:
        print("没找到网络")
    if station.isconnected():
        print(station.ifconfig())

    return True


def fuzaikaiguan():
    tx.value(1)
    M = [0Xaa, 0X55, 0X10, 0X01, 0X00]
    sum = 0
    for i in range(0, len(M)):
        sum += M[i]
    sum = sum & 0xff
    M.append(sum)
    sum = uart.write(bytes(M))
    time.sleep(0.05)
    tx.value(0)


def qingling():
    tx.value(1)
    M = [0Xaa, 0X55, 0X01, 0X00, 0X01]
    sum = 0
    for i in range(0, len(M)):
        sum += M[i]
    sum = sum & 0xff
    M.append(sum)
    sum = uart.write(bytes(M))
    time.sleep(0.01)
    tx.value(0)


sj = 0


async def dispaly(C, s=10):
    global U_k, I_k, P_k, T_k, MR, time_k, f, u, a, p, c, oa, ob, out_k, ca, cb, out_c, ka, kb, kc, error
    try:
        f += 1
        u += ina.voltage()
        a += abs(ina.current())
        p += ina.power()
        c += adc.read()
        if OUT.value() == 1:
            oa += 1
        else:
            ob += 1
        if chongdian.value() == 1:
            ca += 1
        else:
            cb += 1
        if KF.value() == 1:
            ka += 1
        else:
            kb += 1
        if f >= s:
            U = u / s
            I = a / s / 1000
            P = p / s / 1000
            U_k = round(U, 2)
            I_k = round(I, 2)
            P_k = round(P, 2)
            T_k = int(convert_range(c / s, 100, 4000, 0, 80))
            if I_k < 0.05:
                I_k = 0
                P_k = 0
            if U_k < 0.05:
                U_k = 0
            if I_k != 0:
                MR = U_k / abs(I_k)
                if MR > 1000:
                    MR = 1000
            if oa > ob:
                out_k = 1
                sj = 0
            else:
                out_k = 0
            if ca > cb:
                out_c = 1
            else:
                out_c = 0
            if ka > kb:
                kc = 1
            else:
                kc = 0
            f = u = a = p = c = oa = ob = ca = cb = ka = kb = 0
            T = time.localtime(tm)
            time_k = f"{T[3]}:{T[4]}:{T[5]}"
            data_U = "{:0>4s}".format(hex(int(U_k * 100))[2:])
            data_I = "{:0>4s}".format(hex(abs(int(I_k * 100)))[2:])
            data_P = "{:0>4s}".format(hex(int(P_k * 100))[2:])
            data_AH = "{:0>6s}".format(hex(int(Ah_k * 1000))[2:])
            data_WH = "{:0>6s}".format(hex(int(Wh_k * 1000))[2:])
            data_MR = "{:0>4s}".format(hex(int(MR))[2:])
            data_U = hex4_to_hex2(data_U)
            data_I = hex4_to_hex2(data_I)
            data_P = hex4_to_hex2(data_P)
            data_AH = hex6_to_hex2(data_AH)
            data_WH = hex6_to_hex2(data_WH)
            data_MR = hex4_to_hex2(data_MR)
            if C == 1:
                M = [0x55, 0xAA, 0x02]
                M.append(T[3])  # 小时
                M.append(T[4])  # 分钟
                M.append(T[5])  # 秒
                M.append(out_k)  # 输出状态
                M.extend(data_I)  # 显示电流
                M.extend(data_U)  # 显示电压
                M.extend(data_P)  # 显示功率
                M.extend(data_AH)  # 显示安时
                M.extend([0x00])  # 未知
                M.extend(data_WH)  # 显示瓦时
                M.extend([0x00])  # 未知
                M.extend(data_MR)  # 显示内阻
                M.append(T_k)  # 显示温度
                M.extend([0x00])  # 显示OP，OV
                sum = 0
                for i in range(0, len(M)):
                    sum += M[i]
                sum = sum & 0xFF
                M.append(sum)
                # print(M)
                uart.write(bytes(M))  # 将读取的数据写回串口

    except Exception as e:
        error = f"可调电源数据报错：{e}"


async def load(c):
    global U_f, I_f, P_f, Ah_f, Wh_f, T_f, out_f, time_f, error
    try:
        if uart.any():
            data = uart.read()  # 读取数据到缓冲区
#             print("接收到数据：",data)

            if c == 0:
                uart.write(data)  # 将读取的数据写回串口
            if len(data)==26  and data[-1] == sum(data[:-1]) % 256:
                if data[0] == 85 and data[2] == 01:
                    T1 = int.from_bytes(data[3:4], "little")
                    T2 = int.from_bytes(data[4:5], "little")
                    T3 = int.from_bytes(data[5:6], "little")
                    time_f = f"{T1}:{T2}:{T3}"
                    out_f = int.from_bytes(data[6:7], "little")
                    I = int.from_bytes(data[7:9], "little") / 100
                    U = int.from_bytes(data[9:11], "little") / 100
                    P = int.from_bytes(data[11:13], "little") / 100
                    Ah = int.from_bytes(data[13:16], "little") / 1000
                    Wh = int.from_bytes(data[17:20], "little") / 1000
                    I_f = round(I, 2)
                    U_f = round(U, 2)
                    P_f = round(P, 2)
                    Ah_f = round(Ah, 2)
                    Wh_f = round(Wh, 2)
                    T_f = int.from_bytes(data[23:24], "little")
#             else:

#                 print("校验失败")

                    
    except Exception as e:
        error = f"放电仪数据报错：{e}"


def Qling():
    global ah, wh, tm
    qingling()
    ah = 0.0
    wh = 0.0
    tm = 0


def Chongdian(C):
    if C == False and chongdian.value() == 1:
        chongdian.value(0)
        print("充电关")

    elif C == True and chongdian.value() == 0:
        chongdian.value(1)
        print("充电开")


def Fuzai(C):
    if C == False and out_f == 1:
        fuzaikaiguan()
        print("负载关")

    elif C == True and out_f == 0:
        fuzaikaiguan()
        print("负载开")


def Auto(C):
    global auto, xunhuan
    if C == 1:
        print("自动模式")
        auto = 1
        xunhuan = 0
        Qling()
        ktdy_auto()
        Resetcharts()

    else:
        print("手动模式")
        auto = 0
        ktdy_auto()
        Fuzai(False)
        Chongdian(False)


def charts():
    global fasong
    with open("ktdy.txt", "r") as f:
        charts_k = f.read()
    with open("fdy.txt", "r") as f:
        charts_f = f.read()
    keeplive = f"cmd=2&uid={clientID}&topic=ktdy/up&msg=canshu#{xunhuancs}#{charts_k}#{charts_f}\r\n"
    client.send(keeplive.encode("utf-8"))
    fasong = time.time()


def Bfy_client(timer):
    global client, ktdy, auto, fasong, xunhuan, xunhuancs, out_k
    try:
        if station.isconnected() == False:
            print("网络未连接")
            return
        elif not isinstance(client,socket.socket):
            addr_info = socket.getaddrinfo(serverIP, port)
            addr = addr_info[0][-1]
            client = socket.socket(
                socket.AF_INET, socket.SOCK_STREAM
            )  # 创建TCP的套接字,也可以不给定参数。默认为TCP通讯方式
            client.connect(addr)  # 设置要连接的服务器端的IP和端口,并连接
            client.send(("cmd=1&uid=" + clientID +
                        "&topic=ktdy\r\n").encode("utf-8"))
            client.setblocking(False)
        if time.time() >= fasong + 2:
            keeplive = f"cmd=2&uid={clientID}&topic=ktdy/up&msg={"on"if auto else"off"}#{ktdy}#{U_k}#{I_k}#{P_k}#{Ah_k}#{Wh_k}#{T_k}#{time_k}#{U_f}#{I_f}#{P_f}#{Ah_f}#{Wh_f}#{T_f}#{time_f}#{out_c}#{out_f}#{out_k}#{error}\r\n"
            client.send(keeplive.encode("utf-8"))
            fasong = time.time()
        try:
            data = client.recv(256)  # 从服务器端套接字中读取1024字节数据
            if len(data) > 60:
                data = data.decode("utf-8")
                print("data:", data)

                if data.find("topic=ktdy&msg=update") > 0:
                    with open('boot_count.txt', 'w') as f:
                        f.write(str(1))
                    print("升级")
                    machine.reset()

                elif data.find("topic=ktdy&msg=Qling") > 0:
                    Qling()

                elif data.find("topic=ktdy&msg=auto-on") > 0:
                    Auto(True)
                elif data.find("topic=ktdy&msg=auto-off") > 0:
                    Auto(False)

                elif data.find("topic=ktdy&msg=fdy-on") > 0:
                    Fuzai(True)
                elif data.find("topic=ktdy&msg=fdy-off") > 0:
                    Fuzai(False)

                elif data.find("topic=ktdy&msg=chongdian-on") > 0:
                    Chongdian(True)
                elif data.find("topic=ktdy&msg=chongdian-off") > 0:
                    Chongdian(False)

                elif data.find("topic=ktdy&msg=out-on") > 0:
                    out_k=True
                elif data.find("topic=ktdy&msg=out-off") > 0:
                    out_k=False

                elif data.find("topic=ktdy&msg=xhcs=") > 0:
                    a = data.find("xhcs=")+5
                    b = data.find("\n", a)
                    xunhuancs = data[a:b]
                    ktdy_auto()
                    charts()
                    
                elif data.find("topic=ktdy&msg=canshu") > 0:
                    charts()
        except Exception as e:
#             print(e)
            return

    except Exception as e:
        client = None


async def button():
    global ktdy, auto, kf, sj, tm, ah, wh, OUT_k
    if OUT_k != out_k:
        if sj > time.ticks_ms()-300 and time.ticks_ms()-100 > sj:
            print("重置充电")
            tm = ah = wh = 0
        sj = time.ticks_ms()
        OUT_k = out_k

    if kf != kc:
        if sj > time.ticks_ms()-300 and time.ticks_ms()-100 > sj:
            if auto == 0:
                Auto(True)
            else:
                Auto(False)
        kf = kc
        sj = time.ticks_ms()
    if auto == 0 and ktdy != kc and sj < time.ticks_ms()-300:
        ktdy = kc
        ktdy_auto()
        print("模式转换")


f5 = 0
# 定义中断处理函数


def timer_interrupt(timer):
    global ah, wh, Ah_k, Wh_k, f5, tm, fasong
    if abs(I_k) > 0:
        tm += 1
        ah = ah + (abs(I_k) / 3600)
        wh = wh + (P_k / 3600)
        with open("ktdy_data.txt", "w") as f:
            f.write(str(ah) + "\n")
            f.write(str(wh) + "\n")
            f.write(str(tm) + "\n")
    Ah_k = round(ah, 2)
    Wh_k = round(wh, 2)
    FAN.duty(int(convert_range(max(T_f, T_k) , 35, 50, 0, 1023)))
#     print(I_k)
#     print(Ah_k)
#     print(Wh_k)
#     print(T_k)
#     print(T_f)
#     print(int(convert_range(max(T_f, T_k) , 35, 50, 0, 1023)))
    
async def async_function_1():
    while True:
        await button()
        await load(ktdy)
        await dispaly(ktdy)
        await asyncio.sleep(0.0001)


async def async_function_2():
    global xunhuan, ktdy, auto, kf
    a = b = 0
    while True:
        if auto == 1:
            if b == 0:
                b = 1
                if out_c == 0 and ktdy == 1:
                    Fuzai(False)
                    await asyncio.sleep(1)
                    Chongdian(True)

                if ktdy == 0 and out_f == 0:
                    Chongdian(False)
                    await asyncio.sleep(1)
                    Fuzai(True)

            if ktdy == 1 and I_k < 0.5 or ktdy == 0 and out_f == 0:
                a += 1
                if a >= 5:
                    a = b = 0
                    xunhuan += 1
                    if ktdy == 1 and I_k < 0.5:
#                         print(Ah_k)
                        with open("ktdy.txt", "a") as f:
                            f.write(str(Ah_k) + ",")
                            f.seek(0)
                            print("充电容量记录", f.read())
                        print("放电")
                        charts()
                        ktdy = 0
                        ktdy_auto()
                        Qling()

                    elif ktdy == 0 and out_f == 0:
#                         print(Ah_f)
                        with open("fdy.txt", "a") as f:
                            f.write(str(Ah_f) + ",")
                            f.seek(0)
                            print("放电容量记录", f.read())
                        print("充电")
                        charts()
                        ktdy = 1
                        ktdy_auto()
                        Qling()
                if xunhuan >= xunhuancs*2:
                    print("关闭自动模式")
                    auto = 0
                    ktdy = 1
                    ktdy_auto()
                    Chongdian(False)
            else:
                a = 0
        else:
            a = b = 0
        await asyncio.sleep(1)


async def main():
    global error
    while True:
        try:
            await asyncio.gather(async_function_1(), async_function_2())
        except Exception as e:
            error = f"main()函数报错：{e}"


def setup():
    try:
        Readcharts()
    except:
        Resetcharts()
    print("连接wifi")
    connect_wlan("Sensen", "15367660")
    # 每隔 1 秒触发一次中断
    timer1.init(period=1000, mode=machine.Timer.PERIODIC,callback=timer_interrupt)
    timer2.init(period=500, mode=machine.Timer.PERIODIC, callback=Bfy_client)


def loop():
    global error
    try:
        setup()
        asyncio.run(main())
    except Exception as e:
        error = f"loop()函数报错：{e}"

if __name__ == "__main__":
    loop()


